package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"os"

	"sync"
	"time"

	"github.com/gordonklaus/portaudio"
)

var input *portaudio.Stream

const sampleRate = 16000
const bufferLen = 8000

var in = make([]int16, bufferLen)
var rdLocker = &sync.Mutex{}
var inBytes = make([]byte, bufferLen*2)

func ErrPanic(err error) {
	if err != nil {
		panic(err)
	}
}

func initAudio() {
	var err error
	err = portaudio.Initialize()
	if err != nil {
		ErrPanic(err)
	}
	input, err = portaudio.OpenDefaultStream(1, 0, sampleRate, len(in), in)
	if err != nil {
		ErrPanic(err)
	}
}

func newWavFile(fn string) *os.File {
	fp, err := os.Create(fn)
	if err != nil {
		panic(err)
	}
	hdr := createWavHeader()
	fp.Write(hdr)
	return fp
}

var hdrSize int32

func endWaveFile(fp *os.File, dataSize int32) {
	var sizeD8 int32 = dataSize + hdrSize - 8
	var buf [4]byte
	binary.LittleEndian.PutUint32(buf[:], uint32(sizeD8))
	fp.Seek(4, 0)
	fp.Write(buf[:])
	binary.LittleEndian.PutUint32(buf[:], uint32(dataSize))
	fp.Seek(40, 0)
	fp.Write(buf[:])
}

func createWavHeader() []byte {
	hdr := bytes.NewBufferString("RIFF")
	//int size-8  = FileSize - 8
	hdr.Write([]byte{0, 0, 0, 0})
	//wave = "WAVE"
	//fmt = "fmt "
	hdr.WriteString("WAVEfmt ")
	//int fmt_size = 16
	hdr.Write(bytesInt32(16))
	//short int format_tag = 1
	hdr.Write(bytesInt16(1))
	//short int       channels=1
	hdr.Write(bytesInt16(1))
	//int				samples_per_sec==16000
	hdr.Write(bytesInt32(16000))
	//int				avg_bytes_per_sec=32000
	hdr.Write(bytesInt32(32000))
	//short int       block_align=2
	hdr.Write(bytesInt16(2))
	//short int       bits_per_sample=16
	hdr.Write(bytesInt16(16))
	//data = "data"
	hdr.WriteString("data")
	//int				data_size = FileSize - 44
	hdr.Write([]byte{0, 0, 0, 0})
	hdrSize = int32(hdr.Len())
	return hdr.Bytes()
}

func bytesInt32(v int32) []byte {
	var buf [4]byte
	binary.LittleEndian.PutUint32(buf[:], uint32(v))
	return buf[:]
}

func bytesInt16(v int16) []byte {
	var buf [2]byte
	binary.LittleEndian.PutUint16(buf[:], uint16(v))
	return buf[:]
}

func startRecord(filename string, ctrl chan int8) error {

	rdLocker.Lock()
	defer rdLocker.Unlock()

	name := filename
	input.Start()
	defer input.Stop()
	writer := newWavFile(name)
	defer writer.Close()

	var fsize int32

	timeout := time.After(time.Second * 20)
loop:
	for {
		err := input.Read() // 读取数据到缓冲区
		if err != nil {
			fmt.Println("读取数据失败:", err)
			return err
		}
		for i := 0; i < bufferLen; i++ {
			binary.LittleEndian.PutUint16(inBytes[i*2:i*2+2], uint16(in[i]))
		}
		n, err := writer.Write(inBytes)
		if err != nil {
			fmt.Println("保存数据失败:", err)
			return err
		}
		fsize += int32(n)

		select {
		case <-ctrl:
			//println("录音结束")
			break loop
		case <-timeout:
			//要防止执行后面的 ctrl <- 1
			println("录音超时，自动结束")
			endWaveFile(writer, fsize)
			return nil
		default:
		}
		//println("record...")
	}
	endWaveFile(writer, fsize)
	ctrl <- 1
	return nil
}
